﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace osDinamicMemory
{
    [Serializable]
	public class Sections
    {
        public int Size;
		public int Allocation;

		public Sections(int size, int allocation)
		{
			Size = size;
			Allocation = allocation;
		}
	}

	public class FakeMemory
	{
		public byte[] memory;
	    public bool[] bitmap;

	    private List<Sections> allocatedBlocks;
	    private List<Sections> emptyBlocks; 
	    private double blocksize;
	    private int size;
	    private double bitmapSize;

		public FakeMemory(double blocksize)
		{
			memory = new byte[256];
            this.blocksize = blocksize;
            size = (memory.Length / 4);
		    bitmapSize = (memory.Length - size)/blocksize;
            bitmap = new bool[(int)bitmapSize];
            allocatedBlocks = new List<Sections>();
            emptyBlocks = new List<Sections>();
		}
		
		public int MemoryAllocation(int newBlockSize)
		{
            ReadMapFromMemory();
		    double numbersBlock = Math.Ceiling((newBlockSize / blocksize));
            if (numbersBlock > bitmap.Length)
            {
                Console.WriteLine();
                return -1;
            }
            
		    var listEmpty = GetSection();
		    listEmpty = listEmpty.Where(p => p.Size >= numbersBlock).ToList();
		    Sections section = listEmpty.Find(p => p.Size == listEmpty.Min(l => l.Size));
            if (section == null)
            {
                Console.WriteLine("");
                return -1;
            }
		    int allocation = section.Allocation;
            Mark(allocation, (int)numbersBlock + allocation, true);
            Mark(allocation * 8, (((int)numbersBlock + allocation) * 8), 1);
            writeMapToMemory();
		    return allocation*8;
		}

		public void CleanupMemory(int location)
		{
            ReadMapFromMemory();
		    int sizeBlock = memory[location];
		    int sizeBitmapBlock = (int)(sizeBlock/blocksize);
		    int locationInBitMap = (int)(location/blocksize);
            UnMark(location, sizeBlock + location, 0);
            Mark(locationInBitMap, sizeBitmapBlock + locationInBitMap, false);
            writeMapToMemory();
		}

        public void Mark(int i, int j, byte value)
        {
            int board = i + 1;
            if (j == i)
            {
                memory[i] = 1;
            }
            else
            {
                if (i == 0)
                {
                    memory[i] = (byte)(j - i);
                    while (board < j)
                    {
                        memory[board] = value;
                        board++;
                    }
                }
                else
                {
                    memory[i] = (byte)(j - i);
                }

               
                while (board < j)
                {
                    memory[board] = value;
                    board++;
                }
            }
           
        }

        public void Mark(int i, int j, bool value)
        {
            if (j == i)
            {
                bitmap[i] = true;
            }
            else
            {
                int board = i;
                while (board < j)
                {
                    bitmap[board] = value;
                    board++;
                }
            }
        }

        private void UnMark(int i, int j, byte value)
        {
            int board = i;
            while (board < j)
            {
                memory[board] = value;
                board++;
            }
        }

		public List<Sections> GetFullSections()
		{
			return this.GetSectionFullInfo();
		}

        public List<Sections> GetSection()
        {
            var result = new List<Sections>();
            int i = 0;

            while (i < bitmap.Length)
            {
                if (bitmap[i] == false)
                {
                    int size = 1;
                    int j = i + 1;
                    while (j != bitmap.Length + 1)
                    {
                        if (j == bitmap.Length)
                        {
                            j -= 1;
                            break;
                        }

                        if (bitmap[j])
                        {
                            break;
                        }

                        if (bitmap[j] == false)
                        {
                            size++;
                        }

                        j++;
                    }

                    result.Add(new Sections(size, i));
                    i = j;
                }

                i++;
            }

            return result;
        }

        private static bool[] ByteToBits(byte e)
        {
            var bitarr = new bool[8];
            for (int bits = 0; bits < 8; bits++)
                bitarr[7 - bits] = ((e & (1 << bits)) != 0);
            return bitarr;
        }

        private static byte FastConvToByte(bool[] bits)
        {
            Array.Reverse(bits);
            if (bits.Length > 8)
                throw new ArgumentException("Array can contain maximum 8 values!");
            byte result = 0;
            for (byte i = 0; i < bits.Length; i++)
                if (bits[i])
                    result |= (byte)(1 << i);
            return result;
        }


        public void ReadMapFromMemory()
        {
            bool[] tmp1 = new bool[8];
            bool[] tmp2 = new bool[8];
            bool[] tmp3 = new bool[8];

            tmp1 = ByteToBits(memory[memory.Length - 3]);
            tmp2 = ByteToBits(memory[memory.Length - 2]);
            tmp3 = ByteToBits(memory[memory.Length - 1]);
            int i = 0;
            while (i < bitmap.Length)
            {
                for (int j = 0; j < tmp1.Length; j++)
                {
                    bitmap[i] = tmp1[j];
                    i++;
                }

                for (int j = 0; j < tmp2.Length; j++)
                {
                    bitmap[i] = tmp2[j];
                    i++;
                }

                for (int j = 0; j < tmp3.Length; j++)
                {
                    bitmap[i] = tmp3[j];
                    i++;
                }
            }
        }

	    public void writeMapToMemory()
        {
            bool[] tmp1 = new bool[8];
            bool[] tmp2 = new bool[8];
            bool[] tmp3 = new bool[8];
            int i = 0;
            int a, b, c;
            a = b = c = 0;
            while (i < bitmap.Length)
            {
                if (i < 8)
                {
                    tmp1[a] = bitmap[i];
                    a++;
                }

                if (i >= 8 && i < 16)
                {
                    tmp2[b] = bitmap[i];
                    b++;
                }

                if (i > 15)
                {
                    tmp3[c] = bitmap[i];
                    c++;
                }
                i++;
            }

            memory[memory.Length - 3] = FastConvToByte(tmp1);
            memory[memory.Length - 2] = FastConvToByte(tmp2);
            memory[memory.Length - 1] = FastConvToByte(tmp3);

        }


		public List<Sections> GetEmptySections()
		{
			return this.GetSectionEmptyInfo();
		}

		private List<Sections> GetSectionFullInfo()
		{
			var section = new List<Sections>();
			int i = 0;
			while (i < this.memory.Length - this.size)
			{
				if (this.memory[i] > 0)
				{
					int size = this.memory[i];
					int allocation;
				    allocation = i;
				    section.Add(new Sections(size, allocation));
					i = size + i;
					continue;
				}
				else
				{
					i++;
				}
			}

			return section;
		}

		private List<Sections> GetSectionEmptyInfo()
		{
			int i = 0;
			var sectionses = new List<Sections>();
			while (i < this.memory.Length - this.size)
			{
				if (this.memory[i] == 0)
				{
				    int allocation;
				    allocation = i;
				    int j = i;
					int size = 1;

					while ((this.memory[j] == 0) && (j != this.memory.Length - this.size))
					{
						size++;
						j++;
					}

					i = i + size;
					sectionses.Add(new Sections(size - 1, allocation));
					continue;
				}

				i++;
			}

			return sectionses;
		}
	}
}
